// 此仓库是与用户相关的小仓库
// 创建仓库↓
import {defineStore} from 'pinia'
// 导入“请求用户登录”和“发送请求以获取用户信息”、“用户退出登录”时需要用到的方法↓
import {requestLogin,requestUserInfo,requestLogout} from '@/api/user'
// 导入请求用户登录时提交的参数的数据类型↓
// import type {typeOfLoginParams,typeOfPropertyNamedDataInTheResponceDataOfLogin} from '@/api/user/type'
import type { typeOfLoginParams, typeOfResponceDataOfLogin, typeOfResponceDataOfGetUserInfo } from "@/api/user/type";
import type {datatypeForStateOfUser} from '@/store/modules/types/type'
// 导入在本地存储中存取、删除数据的方法↓
import {SET_TOKEN,GET_TOKEN,REMOVE_TOKEN} from '@/utils/token'
// 导入常量路由↓
// import {constantRoute,asyncRoute,anyRoute} from '@/router/routes'
// 导入常量路由、异步路由和任意路由↓
import {constantRoute,asyncRoute,anyRoute} from '@/router/routes'
import router from '@/router' // 导入路由器实例对象
// @ts-ignore
import cloneDeep from 'lodash/cloneDeep' // 导入“lodash”库中的克隆函数，以便在后续对路由规则进行深拷贝时使用
/**
 * 用于过滤当前用户需要展示的异步路由的函数
 */
function filterAsyncRoute(asyncRoutes:any[],menuPermissionsThatCurrentUserHaveHad:any[]) {
    return asyncRoutes.filter((route) => {
        // 注：此处使用了“递归”的方式来过滤当前用户需要展示的异步路由↓
        if (menuPermissionsThatCurrentUserHaveHad.includes(route.name)) {
            // 关键代码：在某个路由有且至少有一个子路由时递归调用此函数，对子路由进行筛选，并把筛选结果作为当前路由的子路由（注：此处必须赋值，否则筛选结果将没有作用）↓
            if (route.children && route.children.length > 0) {
                route.children = filterAsyncRoute(route.children,menuPermissionsThatCurrentUserHaveHad)                
            }
            return true; // 如果当前路由的名称（即“route.name”）在用户拥有的菜单权限信息数组中，则返回true            
        }
    })
}
let useUserStore = defineStore('user',{
    // 这里是仓库中被用于存储数据的地方（注：小括号后面的冒号被用于标注“函数返回值的TS类型”）↓
    state:():datatypeForStateOfUser=>{
        return {
            // token:'' // 属性“token”被用于存储用户登录后返回的token值，这个值在后续发送请求时将会被携带在请求头中。它是用户的身份标识（唯一标识符，在作用上相当于ID）
            // 从本地存储中获取token，以便在页面刷新后，仓库中的token值不会丢失↓
            // token:localStorage.getItem('TOKEN'),
            token:GET_TOKEN('TOKEN'),
            routesDisplayInMenu:constantRoute, // 在仓库中存储生成导航菜单时需要用到的路由规则所在的数组
            username:"",
            avatar:"",
            buttons:[], // 注：此处使用了“数组”来存储按钮权限信息，以便后续在页面中根据用户拥有的按钮权限信息动态渲染按钮            
        }
    },
    // 这里是仓库中被用于修改数据或发送请求，处理异步操作的地方↓
    actions:{        
        /**
         * 用于处理用户登录的函数
         */
        async aFunctionForHandleUserLogin(formData:typeOfLoginParams){
        // async aFunctionForHandleUserLogin(formData:any){
            // console.log('处理用户登录的函数被调用了');
            // console.log(formData);
            // let result:any = await requestLogin(formData)
            // let result:typeOfPropertyNamedDataInTheResponceDataOfLogin = await requestLogin(formData)
            // let result:any = await requestLogin(formData)
            let result:typeOfResponceDataOfLogin = await requestLogin(formData)
            console.log(result)
            if (result.code === 200) {
                // 在登录成功的情况下，将服务器返回的token值存储到仓库中↓
                // 注：开发者在此处使用了“类型断言”（即“as 数据类型”语法）来将服务器返回的token值断言为字符串类型，这样做相当于跟VSCode和TS的类型推断和检查系统说：“我确定服务器返回的token值的数据类型是字符串，且只有在这个token值的数据类型是字符串的情况下，它才能被赋值给仓库中的属性token”↓
                // this.token = (result.data.token as string)
                // 注：把登录接口从“mock接口”改为使用真实的接口后，由于服务器返回的数据中，属性“data”的值本身就是“token”，不再具有“token”这个属性，因此开发者在此处改用下面的这一行代码↓
                this.token = (result.data as string)
                // 注：由于集中式状态管理工具（如“pinia”、“vuex”等）只是利用普通的JavaScript对象来存储数据，它们不能持久化存储数据（即在页面刷新后，数据会丢失），因此如果要持久化存储数据，就需要借助浏览器的本地存储（即“localstorage”）提供的API来实现↓
                // 将token值存储到浏览器的本地存储中↓
                // localStorage.setItem('TOKEN',result.data.token as string)
                // SET_TOKEN((result.data.token as string))
                SET_TOKEN((result.data as string))
                return "ok" // 注：这行语句能保证当前函数（即“aFunctionForHandleUserLogin”）可以返回一个被解析成功的Promise对象，这对于后续的异步操作来说是必须的
            } else {
                // return Promise.reject(new Error(result.data.message))
                // 注：把登录接口从“mock接口”改为使用真实的接口后，由于服务器返回的数据中，属性“data”的值本身就是“错误提示信息”，不再具有“message”这个属性，因此开发者在此处改用下面的这一行代码↓
                return Promise.reject(new Error(result.data))
            }
        },
        /**
         * 函数“getUserInfomation”被用于“获取用户信息”
         */
        async getUserInfomation(){
            console.log('已发送请求获取用户信息');
            // 发送请求以获取用户信息（如“用户头像”，“用户名称”等），并把它们存储到这个仓库中↓
            // let result:any = await requestUserInfo()
            let result:typeOfResponceDataOfGetUserInfo = await requestUserInfo()
            console.log(result);
            console.log(result.data.routes); // Array(40)（打印出该用户拥有的全部菜单权限信息）
            console.log(router.getRoutes()) // 注：由于此时异步路由和任意路由还未被注册，因此此处打印出来的路由规则数组中将只有常量路由
            // 在“获取用户信息成功”的情况下，将服务器返回的用户信息存储到仓库中↓
            if (result.code === 200) {
                // 注：把获取用户信息需要用到的接口从“mock接口”改为使用真实的接口后，由于服务器返回的数据中的属性发生了变化，因此开发者在此处改用下面的未被注释的代码↓
                // this.username = result.data.checkUser.username
                this.username = result.data.name
                // this.avatar = result.data.checkUser.avatar
                this.avatar = result.data.avatar
                this.buttons = result.data.buttons
                // 筛选出需要为当前用户展示的异步路由↓
                // let asyncRoutesWillBeShownForCurrentUser = filterAsyncRoute(asyncRoute,result.data.routes)
                // 注：先把原始异步路由数组进行深拷贝，然后再对拷贝后的副本进行筛选（以免后续赋值时直接修改原始异步路由数组，导致某用户登出后其他用户接续登录时需要用到的异步路由的原始数据出错）↓
                let asyncRoutesWillBeShownForCurrentUser = filterAsyncRoute(cloneDeep(asyncRoute),result.data.routes)
                // 把异步路由、常量路由和任意路由合并到一起，作为菜单中最终将展示的路由，存储到仓库中↓
                this.routesDisplayInMenu = [...constantRoute,...asyncRoutesWillBeShownForCurrentUser,...anyRoute]; // 注：不知何故，此处必须加分号结束这行语句，否则下面的语句将被标红报错
                // 追加异步路由和任意路由（使用Array.forEach方法）↓
                [...asyncRoutesWillBeShownForCurrentUser,...anyRoute].forEach((route:any) => {
                    router.addRoute(route)
                })
                console.log(router.getRoutes()) // 注：此处打印出来的路由规则数组中将包含常量路由、异步路由和任意路由，这说明异步路由和任意路由已经被成功注册到路由器实例对象中
                return "ok"
            } else {
                // return Promise.reject(new Error("获取用户信息失败"))
                return Promise.reject(new Error(result.message))
            }
        },
        /**
         * 用于处理用户登出（退出登录）的函数
         */
        async afunctionForHandleUserLogout(){
            // 发送请求以退出登录（注：“result”后面的冒号后面的“any”的所处位置被用来标示“服务器返回数据”的“TS数据类型”，目前为“any”表示“可以是任何类型”）↓
            let result:any = await requestLogout()
            console.log(result);
            if (result.code == 200) {
                // 注：如果进入了此代码块，则说明用户登出（退出登录）操作成功↓
                // 将仓库中的“token”、“avatar”等“与用户相关的数据”清空↓
                this.token = ''
                this.username = ''
                this.avatar = ''
                // 将本地存储中保存的token值也一并删除↓
                // localStorage.removeItem('TOKEN')
                REMOVE_TOKEN('TOKEN')
                // 在此处返回一个被解析成功的Promise对象（以供组件去判断本次“退出登录”操作的结果是“成功”还是“失败”）↓
                return "ok"
            } else {
                return Promise.reject(new Error(result.message))
            }
        }
    },
    // 这里是仓库中被用于计算数据的地方↓
    getters:{}
})
// 对外导出获取仓库实例的方法↓
export default useUserStore;